#pragma once

#include <efi/memory.hpp>
#include <efi/rt.hpp>

#define EFI_AUDIO_IO_PROTOCOL_GUID                                             \
  {                                                                            \
    0xF05B559C, 0x1971, 0x4AF5, {                                              \
      0xB2, 0xAE, 0xD6, 0x08, 0x08, 0xF7, 0x4F, 0x70                           \
    }                                                                          \
  }

namespace audio {

typedef struct {
  uint16_t channelCount;
  uint16_t bitsPerSample;
  uint32_t samplesPerSec;
} AudioFormat;

typedef struct {
  efi::PoolObject<uint8_t> rawData;
  AudioFormat format;

  void *samples;
  size_t size;
} Audio;

enum class AudioIoType : uint32_t {
  Output,
  Input
};
enum class AudioDeviceEnum : uint32_t {
  Line,
  Speaker,
  Headphones,
  Spdif,
  Mic,
  Hdmi,
  Other
};
enum class AudioDeviceLocation : uint32_t {
  None,
  Rear,
  Front,
  Left,
  Right,
  Top,
  Bottom,
  Other
};
enum class AudioIoSurface : uint32_t {
  External,
  Internal,
  Other
};

typedef enum {
  Freq8kHz = BIT(0),
  Freq11kHz = BIT(1),
  Freq16kHz = BIT(2),
  Freq22kHz = BIT(3),
  Freq32kHz = BIT(4),
  Freq44kHz = BIT(5),
  Freq48kHz = BIT(6),
  Freq88kHz = BIT(7),
  Freq96kHz = BIT(8),
  Freq192kHz = BIT(9)
} SampleFrequency;

typedef enum {
  Bits8 = BIT(0),
  Bits16 = BIT(1),
  Bits20 = BIT(2),
  Bits24 = BIT(3),
  Bits32 = BIT(4)
} SampleBits;

typedef struct {
  AudioIoType type;
  SampleBits supportedBits;
  SampleFrequency supportedFreqs;

  AudioDeviceEnum device;
  AudioDeviceLocation location;
  AudioIoSurface surface;
} AudioIoPort;

#define MAX_CHANNELS 16
#define MAX_VOLUME 100

typedef struct AudioIo {
  typedef void(EFIAPI *Callback)(struct AudioIo *instance, void *context);

  typedef efi::Status(EFIAPI *GetOutputs)(
      struct AudioIo *instance,
      AudioIoPort **outputPorts,
      uint64_t *outputPortsCount
  );

  typedef efi::Status(EFIAPI *SetupPlayback)(
      struct AudioIo *instance,
      uint8_t outputIndex,
      uint8_t volume,
      SampleFrequency samplesPerSecond,
      SampleBits bitsPerSample,
      uint8_t channels
  );

  typedef efi::Status(EFIAPI *StartPlayback)(
      struct AudioIo *instance,
      void *data,
      size_t dataLength,
      uint64_t position
  );

  typedef efi::Status(EFIAPI *StartPlaybackAsync)(
      struct AudioIo *instance,
      void *data,
      size_t dataLength,
      uint64_t position,
      Callback callback,
      void *context
  );

  typedef efi::Status(EFIAPI *StopPlayback)(struct AudioIo *instance);

  GetOutputs getOutputs;
  SetupPlayback setupPlayback;
  StartPlayback startPlayback;
  StartPlaybackAsync startPlaybackAsync;
  StopPlayback stopPlayback;
} AudioIo;

extern uint8_t volume;
extern SampleFrequency samplesPerSecond;
extern SampleBits bitsPerSample;
extern uint8_t channels;

void selectDefaultDevice();

void updatePlaybackConfig();

void play(Audio audioData);
void play(void *data, size_t length, uint64_t position);
void playAsync(void *data, size_t length, uint64_t position);
void stop();

} // namespace audio